package main

import (
	"context"
	"errors"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"time"

	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func main() {
	if err := run(); err != nil {
		log.Fatalln(err)
	}
}

func run() (err error) {
	// 创建一个上下文监听 SIGINT 信号（即 ctrl + c）
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()

	// 初始化 otel 各项
	otelShutdown, err := setupOTelSDK(ctx)
	if err != nil {
		return
	}
	// otelShutdown 用于清理已初始化的 otel 资源
	defer func() {
		err = errors.Join(err, otelShutdown(context.Background()))
	}()

	// Start HTTP server.
	srv := &http.Server{
		Addr:         ":8080",
		BaseContext:  func(_ net.Listener) context.Context { return ctx }, // 使用上面创建好的 ctx，这样按下 ctrl c 后所有服务都能感知到，实现优雅关机
		ReadTimeout:  time.Second,
		WriteTimeout: 10 * time.Second,
		Handler:      newHTTPHandler(),
	}
	srvErr := make(chan error, 1)
	go func() {
		srvErr <- srv.ListenAndServe()
	}()

	// 等待终止信号
	select {
	case err = <-srvErr:
		// HTTP 服务启动失败，返回 err
		return
	case <-ctx.Done():
		// 等待 SIGINT 信号，即 ctrl c
		// 监听到信号后执行 stop，停止监听信号
		stop()
	}

	// 优雅关机
	err = srv.Shutdown(context.Background())
	return
}

// newHTTPHandler 创建 HTTP 处理器，给每个路由都启用 otel 的 HTTP 路由追踪
func newHTTPHandler() http.Handler {
	mux := http.NewServeMux()

	// handleFunc 是一个服务函数，用于将路由和处理器注册到 mux
	handleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) {
		// 给处理器装上 otel 的 http route 标签，帮助追踪器识别请求的具体路由
		handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc))
		mux.Handle(pattern, handler)
	}

	// Register handlers.
	handleFunc("/rolldice/", rolldice)
	handleFunc("/rolldice/{player}", rolldice)

	// 把 mux 封装成一个支持 otel 的处理器，所有请求都会经过 otel 的 HTTP 抽象层
	handler := otelhttp.NewHandler(mux, "/")
	return handler
}
